/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2017-2020. All rights reserved.
 * Description: om
 */

#ifndef OM_H
#define OM_H

#include <map>
#include <mutex>
#include <memory>
#include <string>
#include <vector>
#include <functional>

#include "infra/om/npu_om_comm.h"
#include "infra/base/api_export.h"

namespace om {
INFRA_API_EXPORT bool BuildConfigString(std::string& configStr, const std::map<std::string, std::string>& configMap);

INFRA_API_EXPORT bool ParseConfigString(std::map<std::string, std::string>& configMap, const std::string& configStr);

INFRA_API_EXPORT bool ValidateConfigKey(const std::string& key);

INFRA_API_EXPORT bool ValidateConfigValue(const std::string& value);

enum EventType {
    NONE_EVENT = -1, // for omit compiler warning
    PROFILING_ON,
    PROFILING_OFF,
    DUMP_ON,
    DUMP_OFF,
    LOG_SETTING,
    ISPNN_PROFILING_ON,
    ISPNN_PROFILING_OFF,
    GLOBAL_PROFILING_ON,
    GLOBAL_PROFILING_OFF,
};

struct EventMsg {
    EventType type;
    int pid; // 客户端进程的pid，仅对该pid下的模型进行过滤处理
    std::map<std::string, std::string> config; // 解析后的扩展选项
    std::uint64_t beginTime;
};

class INFRA_API_EXPORT EventListener {
public:
    virtual ~EventListener()
    {
    }
    virtual void Handle(const EventMsg& event) = 0;
};

using EventListenerPtr = std::shared_ptr<EventListener>;

// OM类
class INFRA_API_EXPORT OM final {
public:
    /*
     * @return OM singleton instance.
     */
    static OM& GetInstance();

    /*
     * Publish a event to message queue.
     * @param event the message to be published.
     */
    void PostEvent(const EventMsg& event);

    /*
     * Register a global listener for all message(without filter), it will handle all event.
     * @param listener the event listener.
     * @return register success or failure.
     */
    bool RegisterListener(const EventListenerPtr& listener);

    /*
     * Flush all events.
     */
    void FlushEvents();

    /*
     * Register a listener for a specific pid, the listener will handle message with a filter that: `msg.pid == pid`.
     * Don't use a previously registered global listener or pid-specific listener.
     * @param pid the specific pid.
     * @param listener the event listener, if the listener already registered, it will return failed.
     * @return a bool value indicates register success or failure.
     */
    bool RegisterListener(pid_t pid, const EventListenerPtr& listener);

    /*
     * Flush events with specific pid.
     * @param pid the specific pid.
     */
    void FlushEvents(pid_t pid);

private:
    using EventFilter = std::function<bool(const EventMsg&)>;
    using WeakListener = std::pair<std::weak_ptr<EventListener>, EventListener*>; // inter class use only.

    void EnqueueEvent(const EventMsg& event);

    bool RegisterListenerLocked(const EventListenerPtr& listener);

    // no standalone thread call this, PostEvent and RegisterListener will call it.
    void HandlePendingEvents(pid_t pid = 0);

    // dispatch events to global listeners.
    void HandleGlobalEvents();

    // dispatch events to pid-specific listeners.
    void HandleProcessEvents(pid_t pid);

    // default event filter for global listener.
    void HandleEventsWith(const EventListenerPtr& listener, const EventFilter& filter = {});

    void RemoveInactiveListeners();

    // disallow copy and assignment.
    OM(const OM&) = delete;
    OM& operator=(const OM&) = delete;

    // singleton, disallow create instance out of class.
    OM();
    ~OM();

private:
    mutable std::mutex mutex_;
    std::vector<EventMsg> pendingEvents_;
    std::vector<WeakListener> globalListeners_;
    std::multimap<pid_t, WeakListener> processListeners_; // for server side useage.
    std::map<EventListener*, size_t> dispatchedEvents_; // number of events each listener dispatched.
};
} // namespace om

#endif // OM_H
